#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <unistd.h>
#include <sys/types.h>
#include <dirent.h>
#include <string.h>
#include <pthread.h>
#include <math.h>
#define DT_DIR 4

typedef struct Stat {
    pid_t pid;                  // 进程号
    char name[100];             // 程序or命令名称
    char task_state;            // 进程状态
    pid_t ppid;                 // 父进程id
    unsigned long pgid;          // 进程组号
    unsigned long sid;           // 该进程所在的会话组id
    unsigned long tty_pgrp;
    unsigned long flags;
    unsigned long min_fit;
    unsigned long cmin_fit;
    unsigned long maj_fit;
    unsigned long cmj_fit;
    unsigned long utime;
    unsigned long stime;
    unsigned long cutime;
    unsigned long cstime;
    unsigned long priority;
    unsigned long nice;

}Stat;

unsigned long long total_memory;
pthread_t threads[500];
int indx = 0;

// 获取系统的时钟滴答数
long get_clock_ticks() {
    long ticks = sysconf(_SC_CLK_TCK);
    return ticks;
}

double cal_cpu_usage(pid_t pid) {
    char stat_path[40];
    FILE *fp;
    long long utime1, stime1, cutime1, cstime1, utime2, stime2, cutime2, cstime2;
    double total_time1, total_time2, cpu_usage;

    sprintf(stat_path, "/proc/%d/stat", pid);

    fp = fopen(stat_path, "r");
    fscanf(fp, "%*lld %*s %*s %*lld %*lld %*lld %*lld %*lld %*lld %*lld %*lld %*lld %*lld %lld %lld %lld %lld", &utime1, &stime1, &cutime1, &cstime1);
    fclose(fp);

    sleep(1);

    fp = fopen(stat_path, "r");
    fscanf(fp, "%*lld %*s %*s %*lld %*lld %*lld %*lld %*lld %*lld %*lld %*lld %*lld %*lld %lld %lld %lld %lld", &utime2, &stime2, &cutime2, &cstime2);
    fclose(fp);

    total_time1 = utime1 + stime1 + cutime1 + cstime1;
    total_time2 = utime2 + stime2 + cutime2 + cstime2;

    cpu_usage = ((total_time2 - total_time1) / (double)get_clock_ticks()) * 100.0;
    sleep(1);
    return cpu_usage;
}

// 计算系统内存大小
void get_total_memory() {
    FILE *meminfo = fopen("/proc/meminfo", "r");
    char line[256];

    while (fgets(line, sizeof(line), meminfo)) {
        if (strncmp(line, "MemTotal:", 9) == 0) {
            sscanf(line, "MemTotal: %llu kB", &total_memory);
            break;
        }
    }
    fclose(meminfo);
}

// 计算pid进程的内存使用率
double cal_mem_usage(pid_t pid) {
    char status_path[40];
    char line[256];
    double vmrss;

    sprintf(status_path, "/proc/%d/status", pid);
    FILE *fp = fopen(status_path, "r");
    while (fgets(line, sizeof(line), fp)) {
        if (strncmp(line, "VmRSS:", 5) == 0) {
            sscanf(line, "VmRSS: %lf", &vmrss);
            break;
        }
    }
    fclose(fp);

    double memory_usage = vmrss / total_memory * 100;
    return memory_usage;
}

int cal_fd(pid_t pid) {
    char buffer[100];
    sprintf(buffer, "/proc/%d/fd", pid);

    DIR* dir;
    struct dirent *fd_dir;
    dir = opendir(buffer);

    int fd_num = 0;
    while((fd_dir = readdir(dir)) != NULL) {
        if(fd_dir->d_type != DT_DIR) {
            continue;
        }
        if(strcmp(fd_dir->d_name, ".") == 0 || strcmp(fd_dir->d_name, "..") == 0) {
            continue;
        }
        fd_num++;
    }

    return fd_num;
}

// 判断字符串是否均为数字
bool is_all_numbers(char* proc) {
    int len = strlen(proc);
    for(int i = 0; i < len; i++) {
        if(!(proc[i] >= '0' && proc[i] <= '9')) {
            return false;
        }
    }
    return true;
}

// 获取进程可执行程序所在路径
void get_path(pid_t pid, char* link, int sz) {
    char path[100] = {'\0'};
    sprintf(path, "/proc/%d/exe", pid);
    if(readlink(path, link, sz - 1) == -1) {
        sprintf(link, "null path");
    }
}

// 线程函数
void* sub_thread_run(void* args) {
    pid_t pid = *((int*)args);
    char link[100];
    get_path(pid, link, sizeof(link));
    printf("%d\tfd:%d\tcpu:%.2lf\tmem:%.2lf\tpath:%s\n", pid, cal_fd(pid), cal_cpu_usage(pid), cal_mem_usage(pid), link);
}

void start() {
    pid_t pids[500];
    DIR *dir;
    struct dirent *proc_dir;

    get_total_memory();

    dir = opendir("/proc");
    while((proc_dir = readdir(dir)) != NULL) {
        if(proc_dir->d_type == DT_DIR) {
            if(strcmp(".", proc_dir->d_name) == 0 || strcmp("..", proc_dir->d_name) == 0) {
                continue;
            }
            if(!is_all_numbers(proc_dir->d_name)) {
                continue;
            }
            pids[indx] = atoi(proc_dir->d_name);
            pthread_create(&threads[indx++], NULL, sub_thread_run, (void*)&pids[indx]);
        }
    }
    for(int i = 0; i < indx; i++) {
        pthread_join(threads[i], NULL);
    }
    indx = 0;
}

int main() {
    if(getegid() != 0) {
            printf("tips : 请以root权限执行该程序!\n");
            return 1;
    }
    while(true) {
        start();
        printf("============================================\n");
        sleep(10);
    }
    return 0;
}